﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Trippy.CollidableGameComponents;
using Trippy.CollidableGameComponents.Agents;
using Trippy.Levels;

namespace Trippy.Camera
{
    public class TrippyCamera : GameComponent
    {
        [Flags]
        private enum CameraState {Tracking = 1, Zooming = 2, Shifting = 4, } // tracking target, zooming on target, switching targets

        public delegate void OnFinish();

        private OnFinish function;

        private CameraState state = CameraState.Tracking;

        /// <summary>
        /// How much the camera is zoomed in
        /// </summary>
        public float Zoom { get; protected set; }

        private float zoomSpeed = 0;
        private float targetZoom = 1f;

        private float movementSpeed = 0;
        private CollidableGameComponent newTarget = null;

        /// <summary>
        /// How much the camera is rotated (radians)
        /// </summary>
        public float Rotation { get; protected set; }

        /// <summary>
        /// The position the camera is looking at
        /// </summary>
        public Vector2 Position { get; protected set; }
        
        /// <summary>
        /// The game component the camera is locked on to
        /// </summary>
        public CollidableGameComponent Focus { get; protected set; }

        public Matrix Transform { get; protected set; }

        public Level CurrentLevel { get; protected set; }

        public TrippyCamera(Level level, CollidableGameComponent focus) : this(level, focus, 1.0f, 0.0f)
        {
        }

        public TrippyCamera(Level level, CollidableGameComponent focus, float zoom, float rotation) : base (level.Game)
        {
            CurrentLevel = level;
            Focus = focus;
            Zoom = zoom;
            Rotation = rotation;
            Position = focus.Position;
            CalculateMatrix();
        }

        public override void Update(GameTime gameTime)
        {
            if ((state & CameraState.Tracking) == CameraState.Tracking)
            {
                if (Focus != null)
                {
                    InstantShift(Focus);
                }
            }
            if ((state & CameraState.Shifting) == CameraState.Shifting)
            {
                    Vector2 direction = Vector2.Normalize(newTarget.Position - Position);
                    Vector2 movement = direction*movementSpeed*TrippyUtilities.CalculateElapsedTime(gameTime);

                    Position += movement;
                    // we are within 1 step of the right position, so let's set it and move on
                    if (Vector2.DistanceSquared(Position, newTarget.Position) < movement.LengthSquared())
                    {
                            Position = newTarget.Position;
                            Focus = newTarget;
                            state = state & ~CameraState.Shifting;
                            state = state | CameraState.Tracking;
                            if (function != null)
                            {
                                function();
                            }
                        
                    }
            }
            if ((state & CameraState.Zooming) == CameraState.Zooming)
            {
                    
                    Zoom += zoomSpeed*TrippyUtilities.CalculateElapsedTime(gameTime);

                    // we are within 1 step of the right zoom, so let's set it and move on
                    if ((zoomSpeed >= 0 && Zoom > targetZoom ) || 
                        (zoomSpeed <= 0 && Zoom < targetZoom))
                    {
                        InstantZoom(targetZoom);
                        state = state & ~CameraState.Zooming;
                        if (function != null)
                        {
                            function();
                            function = null;
                        }
                    }
            }

            CalculateMatrix();
            base.Update(gameTime);

        }

        public void RegisterFinishFunction(OnFinish function)
        {
            this.function = function;
        }

        public void DeregisterFinishFunction()
        {
            function = null;
        }

        public void ReleaseTarget()
        {
            Focus = null;
        }

        public void InstantShift(CollidableGameComponent target)
        {
            Focus = target;
            Position = target.Position;
        }

        /// <summary>
        /// Speed is measured in pixels per second
        /// </summary>
        public void LinearShift(CollidableGameComponent target, float speed)
        {
            newTarget = target;
            movementSpeed = speed;
            state = state | CameraState.Shifting;
            state = state & ~CameraState.Tracking;
        }

//         public void LinearShiftAndBack(CollidableGameComponent source, CollidableGameComponent target, float speed)
//         {
//             newTarget = target;
//             movementSpeed = speed;
//             state = state | CameraState.Shifting;
//             state = state | CameraState.Returning;
//             state = state & ~CameraState.Tracking;
//         }

        /// <summary>
        /// Zooms in at a given rate, specified as a float between 0f and 1f that specifies the ammount to increment per update
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rate"></param>
        public void LinearZoom(float zoom, float speed)
        {
            state = state | CameraState.Zooming;
            if (zoom > Zoom)
            {
                zoomSpeed = speed;
            }
            else
            {
                zoomSpeed = -speed;
            }

            targetZoom = zoom;
        }

        public void InstantZoom(float zoom)
        {
            Zoom = zoom;

        }

        private void CalculateMatrix()
        {
            Transform = Matrix.CreateTranslation(-Position.X, -Position.Y, 0.0f) *
                Matrix.CreateRotationZ(Rotation) *
                Matrix.CreateScale(Zoom, Zoom, 1.0f) *
                Matrix.CreateTranslation((float)(Game.GraphicsDevice.Viewport.Width / 2.0f), (float)(Game.GraphicsDevice.Viewport.Height / 2.0f), 0.0f);
        }
    }
}
